﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using GalaSoft.MvvmLight.Messaging;
using KinectCurve.Entities;

namespace KinectCurve.View
{
    public partial class Game : UserControl
    {
        #region private

        private Random randomgenerator;
        private int randomSeed = Environment.TickCount;
        private DispatcherTimer _gameTimer;
        private DispatcherTimer _preGameTimer;
        private List<Player> playerList;

        private Player player1;
        private Player player2;
        private Player player3;
        private Player player4;

        private Key player1Key;
        private Key player2Key;
        private Key player3Key;
        private Key player4Key;

        #endregion

        //TODO: try to move logic to MVVM or some class, we don't like code behind of the view
        public Game()
        {
            InitializeComponent();

            randomgenerator = new Random(randomSeed++);

            MessengerDefinitions();
            InitializePlayers();

            short counter = 0;
            _preGameTimer = new DispatcherTimer();
            _preGameTimer.Interval = TimeSpan.FromSeconds(1);
            _preGameTimer.Tick += (s, e) =>
            {
                counter++;

                gamePrepareText.Text = counter.ToString();

                if (counter == 2)
                {
                    #region initial direction

                    if (randomgenerator.Next(211, 321) % 3 == 0) player1Key = player1.LeftKey;
                    else player1Key = player1.RightKey;

                    if (randomgenerator.Next(123, 421) % 4 == 0) player2Key = player2.RightKey;
                    else player2Key = player2.LeftKey;

                    if (randomgenerator.Next(433, 755) % 4 == 0) player3Key = player3.RightKey;
                    else player3Key = player3.LeftKey;

                    if (randomgenerator.Next(11, 343) % 3 == 0) player4Key = player4.RightKey;
                    else player4Key = player4.LeftKey;

                    if (player1.IsAlive) player1line.Points = DrawPlayer(ref player1, player1Key);
                    if (player2.IsAlive) player2line.Points = DrawPlayer(ref player2, player2Key);
                    if (player3.IsAlive) player3line.Points = DrawPlayer(ref player3, player3Key);
                    if (player4.IsAlive) player4line.Points = DrawPlayer(ref player4, player4Key);

                    Canvas.SetLeft(player1dot, player1.X - 2);
                    Canvas.SetTop(player1dot, player1.Y - 2);
                    Canvas.SetLeft(player2dot, player2.X - 2);
                    Canvas.SetTop(player2dot, player2.Y - 2);
                    Canvas.SetLeft(player3dot, player3.X - 2);
                    Canvas.SetTop(player3dot, player3.Y - 2);
                    Canvas.SetLeft(player4dot, player4.X - 2);
                    Canvas.SetTop(player4dot, player4.Y - 2);

                    #endregion
                }
                else if (counter == 3)
                {
                    gamePrepareText.Text = "GO!";
                }
                else if (counter > 3)
                {
                    _preGameTimer.Stop();
                    StartGame();
                    gamePrepareText.Visibility = Visibility.Collapsed;
                }

            };
            _preGameTimer.Start();
        }

        /// <summary>
        /// Starts the game
        /// </summary>
        private void StartGame()
        {
            _gameTimer = new DispatcherTimer();
            _gameTimer.Interval = TimeSpan.FromMilliseconds(55);
            _gameTimer.Tick += (s, e) =>
            {
                #region debug simulation - comment this out if you want to play
                //todo: make computer smarter

                //if (randomgenerator.Next(211, 321) % 3 == 0) player1Key = player1.LeftKey;
                //else player1Key = player1.RightKey;

                if (randomgenerator.Next(123, 421) % 4 == 0) player2Key = player2.RightKey;
                else player2Key = player2.LeftKey;

                if (randomgenerator.Next(433, 755) % 3 == 0) player3Key = player3.RightKey;
                else player3Key = player3.LeftKey;

                if (randomgenerator.Next(11, 343) % 3 == 0) player4Key = player4.RightKey;
                else player4Key = player4.LeftKey;

                #endregion

                //todo: what if only two players play?
                if (player1.IsAlive) player1line.Points = DrawPlayer(ref player1, player1Key);
                if (player2.IsAlive) player2line.Points = DrawPlayer(ref player2, player2Key);
                if (player3.IsAlive) player3line.Points = DrawPlayer(ref player3, player3Key);
                if (player4.IsAlive) player4line.Points = DrawPlayer(ref player4, player4Key);

                DrawPlayerDot(player1dot, player1.X, player1.Y);
                DrawPlayerDot(player2dot, player2.X, player2.Y);
                DrawPlayerDot(player3dot, player3.X, player3.Y);
                DrawPlayerDot(player4dot, player4.X, player4.Y);

                player1Key = Key.None;
                player2Key = Key.None;
                player3Key = Key.None;
                player4Key = Key.None;

                //check if round ended
                if (playerList.Where(m => m.IsAlive).Count() == 1)
                {
                    gamePrepareText.Text = String.Format("Game ended!\n{0} wins round!", playerList.Where(m => m.IsAlive).Select(m => m.PlayerName).FirstOrDefault());
                    gamePrepareText.Visibility = Visibility.Visible;
                    Messenger.Default.Send<EnumGameEnd>(EnumGameEnd.Normal, "endGame");
                }
            };
            _gameTimer.Start();
        }

        /// <summary>
        /// Draw player dot
        /// </summary>
        /// <param name="element"></param>
        /// <param name="lenght"></param>
        /// <param name="height"></param>
        private void DrawPlayerDot(UIElement element, int lenght, int height)
        {
            Canvas.SetLeft(element, lenght - 2);
            Canvas.SetTop(element, height - 2);
        }

        /// <summary>
        /// Moves player on the canvas
        /// </summary>
        /// <returns></returns>
        private PointCollection DrawPlayer(ref Player player, Key moveKey)
        {
            randomgenerator = new Random(randomSeed++);

            //init position
            if (player.CoordinatePoints == null)
            {
                //start point
                player.X = randomgenerator.Next((int)LayoutRoot.ActualWidth);
                player.Y = randomgenerator.Next((int)LayoutRoot.ActualHeight);

                //don't place player near wall
                if (player.X - 100 <= 0) player.X = player.X + 80;
                if (player.Y - 100 <= 0) player.Y = player.Y + 80;
                if (player.X + 100 >= (int)LayoutRoot.ActualWidth) player.X = player.X - 80;
                if (player.Y + 100 >= (int)LayoutRoot.ActualHeight) player.Y = player.Y - 80;

                var point = new Point() { X = player.X, Y = player.Y };

                //new point
                player.Move(moveKey);
                var point1 = new Point() { X = player.X, Y = player.Y };

                player.CoordinatePoints = new PointCollection();
                player.CoordinatePoints.Add(point);
                player.CoordinatePoints.Add(point1);
            }
            else
            {
                //if player is alive
                if (player.IsAlive)
                {
                    //get new point(position) for player
                    player.Move(moveKey);
                    var point = new Point() { X = player.X, Y = player.Y };

                    //check collision for player
                    player.IsAlive = !CheckCollision(point);

                    //update players score if player dies
                    if (!player.IsAlive)
                    {
                        foreach (var itemPlayer in playerList)
                        {
                            itemPlayer.Score += 2;
                        }
                    }

                    //add point to path
                    player.CoordinatePoints.Add(point);
                }
            }

            return player.CoordinatePoints;
        }

        /// <summary>
        /// register messengers
        /// </summary>
        private void MessengerDefinitions()
        {
            Messenger.Default.Register<Key>(this, "keyPress", input =>
            {
                //System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss") + " keyPress: " + input.ToString());

                //todo: try to find out what is happening if you hold one arrow key, it seem to take too long to capture the button?
                if (input == player1.LeftKey || input == player1.RightKey)
                {
                    player1Key = input;
                }
                else if (input == player2.LeftKey || input == player2.RightKey)
                {
                    player2Key = input;
                }
                else if (input == player3.LeftKey || input == player3.RightKey)
                {
                    player3Key = input;
                }
                else if (input == player4.LeftKey || input == player4.RightKey)
                {
                    player4Key = input;
                }
            });

            Messenger.Default.Register<EnumGameEnd>(this, "endGame", input =>
            {
                if (_preGameTimer != null)
                {
                    _preGameTimer.Stop();
                    _preGameTimer = null;
                }
                if (_gameTimer != null)
                {
                    _gameTimer.Stop();
                    _gameTimer = null;
                }
            });
        }

        /// <summary>
        /// initialize players
        /// </summary>
        private void InitializePlayers()
        {
            if (player1 == null)
            {
                player1 = new Player(1);
                player1.IsAlive = true;
                player1.LeftKey = Key.Left;
                player1.RightKey = Key.Right;
                player1line.StrokeDashArray = player1.GetStrokeDashArray();
            }

            if (player2 == null)
            {
                player2 = new Player(2);
                player2.IsAlive = true;
                player2.LeftKey = Key.S;
                player2.RightKey = Key.D;
                player2line.StrokeDashArray = player2.GetStrokeDashArray();
            }

            if (player3 == null)
            {
                player3 = new Player(3);
                player3.IsAlive = true;
                player3.LeftKey = Key.G;
                player3.RightKey = Key.H;
                player3line.StrokeDashArray = player3.GetStrokeDashArray();
            }

            if (player4 == null)
            {
                player4 = new Player(4);
                player4.IsAlive = true;
                player4.LeftKey = Key.B;
                player4.RightKey = Key.N;
                player4line.StrokeDashArray = player4.GetStrokeDashArray();
            }

            playerList = new List<Player>();
            playerList.Add(player1);
            playerList.Add(player2);
            playerList.Add(player3);
            playerList.Add(player4);
        }

        /// <summary>
        /// checks for collision of new point coordinates and applies stroke thickness
        /// </summary>
        /// <param name="centerPoint"></param>
        /// <returns></returns>
        private bool CheckCollision(Point centerPoint)
        {
            bool functionReturnValue = false;

            //wall collision
            if (centerPoint.X - 1 < 0
                || centerPoint.X + 1 > (int)LayoutRoot.ActualWidth
                || centerPoint.Y - 1 < 0
                || centerPoint.Y + 1 > (int)LayoutRoot.ActualHeight)
            {
                functionReturnValue = true;
            }

            //player collision
            //TODO: ignore line gaps
            if (!functionReturnValue)
            {
                foreach (var player in playerList)
                {
                    //2.6 <- round up
                    for (int i = Convert.ToInt32(centerPoint.X - 2.6); i < centerPoint.X + 2.6; i++)
                    {
                        for (int j = Convert.ToInt32(centerPoint.Y - 2.6); j < centerPoint.Y + 2.6; j++)
                        {
                            var point = new Point() { X = i, Y = j };
                            if (player.CoordinatePoints.Contains(point))
                            {
                                functionReturnValue = true;
                                break;
                            }
                        }
                    }
                }
            }

            return functionReturnValue;
        }
    }
}
